import { GameStage } from "../constant/GameStage";
import { PokerHand } from "../constant/PokerHand";
import PokerManager from "../game/PokerManager";
import PokerUtil from "../utils/PokerUtil";
import RandomUtil from "../utils/RandomUtil";
import Player from "./Player";
import { Poker } from "./Poker";


// export interface IRefereeVo {
//   // 上一副牌
//   preCards: Array<Poker>
//   // // 当前出牌者的下标
//   // cuDealPlayerIndex: number
//   // 下一个出牌者下标
//   // nextDealPlayerIndex: number
//   // 当前回合玩家下标r
//   currentRoundPlayerIndex: number
// }

export type StageCallbackFn = (state: GameStage, data?: any) => void
export type TaskUnit = (player: Player) => void

/**
 * 裁判类
 * 管理房间的游戏流程和规则
 */
export default class Referee {
  private players: Array<Player | undefined> = []         // 玩家集合
  private preCards: Poker[] = []                          // 上一副牌
  // private preCardsHand: PokerHand = PokerHand.NONE        // 上一副牌的牌型
  private prePlayPlayer: Player | null = null   // 上一个出牌玩家（用于判断是否要和上一副牌比较）
  private _landlordCards: Poker[] = []                    // 地主牌
  private currentRoundPlayerIndex: number = -1            // 当前回合的玩家的下标
  private beginCallPlayerIndex: number = -1               // 开始执行【叫地主/不叫地主】的玩家的下标
  private isEndCall: boolean = false
  private _stage: GameStage = GameStage.PREPARATION       // 游戏阶段
  private stageCallbacks: Set<StageCallbackFn | Function> = new Set()
  private _landlordIndex = -1                             // 地主下标
  private _firstCallLandPlayerIndex = -1                  // 第一个叫地主的玩家下标
  private _lastCallLandPlayerIndex = -1                   // 最后一个叫地主的玩家下标
  private _winPlayer: Player | null = null                // 赢家
  private _winPlayerIsLand: boolean = false               // 赢家是否是地主

  constructor(players: Array<Player | undefined>) {
    this.init(players)
  }

  public get stage() {
    return this._stage
  }

  public set stage(gameStage: GameStage) {
    if (this._stage != gameStage) {
      this._stage = gameStage
      this.notifyStageCallback(this._stage)
    }
  }

  public get landlordPlayer() {
    if (this._landlordIndex === -1) {
      return null // 表示没有确定地主（如果是）
    }
    return this.players[this._landlordIndex]
  }

  public get landlordCards(): Poker[] {
    return this._landlordCards
  }

  /**
   * 初始化数据
   */
  public init(players: Array<Player | undefined>) {
    this.players = players
    this.players.forEach(player => {
      if (player != undefined) { player.callLandloardCount = 0 }
    })
    this.preCards = []
    this.prePlayPlayer = null
    this.currentRoundPlayerIndex = -1
    this.beginCallPlayerIndex = -1
    this._landlordIndex = -1
    this.stage = GameStage.PREPARATION  // 准备阶段
    this.isEndCall = false
    this.stageCallbacks = new Set()
    this.clearPrepare() // 取消所有人的准备标志位
    this._landlordCards = []
    this._firstCallLandPlayerIndex = -1                  // 第一个叫地主的玩家下标
    this._lastCallLandPlayerIndex = -1                   // 最后一个叫地主的玩家下标
    this._winPlayer = null                               // 赢家
    this._winPlayerIsLand = false                        // 赢家是否是地主
  }

  /**
   * 发牌
   */
  public deal() {
    // 生成一副扑克牌
    const cards = PokerManager.instance.getDeck()

    // 取出3张地主牌
    this._landlordCards = []
    for (let i = 0; i < 3; i++) {
      const card = cards.pop()
      if (card != undefined) {
        this._landlordCards.push(card)
      }
    }

    // 发牌
    for (let i = 0; i < 3; i++) {
      const player = this.players[i]
      if (player === undefined) {
        throw new Error(`[referee] player is undefined`)
      }
      player.initCards(cards.splice(0, 17)) // 17张牌
      // console.log('[referee] player.cards:', player.cards)
    }

    // 设为发牌阶段
    this.stage = GameStage.REFEREE_DEAL

  }

  /**
   * 初始化叫地主
   * @returns 第一个开始叫地主的玩家
   */
  public initCallLandloard(): Player {
    // 随机设定一个玩家开始叫地主
    const index = RandomUtil.getRangeInt(0, this.players.length)
    this.beginCallPlayerIndex = index
    this.currentRoundPlayerIndex = index
    this.isEndCall = false
    this._firstCallLandPlayerIndex = -1                  // 第一个叫地主的玩家下标
    this._lastCallLandPlayerIndex = -1                   // 最后一个叫地主的玩家下标
    this._landlordIndex = -1
    this._winPlayer = null                               // 赢家
    this._winPlayerIsLand = false                        // 赢家是否是地主

    const player = this.players[this.currentRoundPlayerIndex]
    if (player === undefined) {
      throw new Error(`[referee] player is undefined`)
    }

    // 设为叫地主阶段
    this.stage = GameStage.CALL_LANDLORD

    return player
  }

  /**
   * 设置有人叫地主
   * @param uid 
   */
  public callLandloard(uid: string) {
    const playerIndex = this.findPlayerIndexById(uid)

    if (playerIndex === -1) {
      throw new Error(`[referee] this user does not exist, user id = ${uid}`)
    }

    const player = this.players[playerIndex]
    if (player === undefined) {
      throw new Error(`[referee] player is undefined`)
    }

    // 记录叫地主下标
    if (this._firstCallLandPlayerIndex === -1) {
      this._firstCallLandPlayerIndex = playerIndex  // 第一个叫地主的玩家
    }
    this._lastCallLandPlayerIndex = playerIndex // 最后一个叫地主的玩家

    console.log('callLandloard()')
    console.log('this._firstCallLandPlayerIndex:', this._firstCallLandPlayerIndex)
    console.log('this._lastCallLandPlayerIndex:', this._lastCallLandPlayerIndex)

    player.callLandloardCount++
  }

  /**
   * 根据用户id查找用户的座位
   * @param uid 
   * @returns 座位号
   */
  public findPlayerIndexById(uid: string): number {
    const index = this.players.findIndex(player => {
      return player != undefined && player.id === uid
    })
    return index
  }

  /**
   * 获取下一个叫地主的玩家
   * 说明：
   * - 地主归属权为最后一个执行【叫地主】的玩家；
   * - 所有人都不叫地主，那么第一个开始执行【叫地主/不叫地主】操作的玩家不需要再执行操作，直接返回null，系统要重新发牌
   * - 仅有1人叫地主，那么ta不需要再执行一次【叫地主/不叫地主】操作，直接归属ta为地主，并且函数返回null
   * - 2人及以上（包含所有人）叫地主，那么第一次【叫地主】的玩家，需要再执行一次【叫地主/不叫地主】操作
   * @returns 玩家对象 | null；玩家对象表示下一个执行【叫地主/不叫地主的玩家】；null表示没有下一个玩家了，叫地主环节结束。
   * 可以通过get landlordPlayer() 获取地主玩家对象
   */
  public nextCallLandloardPlayer(): Player | null {
    // 结束叫地主阶段
    if (this.isEndCall) {
      return null
    }

    // 往右遍历为顺时针
    this.currentRoundPlayerIndex++
    if (this.currentRoundPlayerIndex >= this.players.length) {
      this.currentRoundPlayerIndex = 0
    }

    // 表示轮完了一圈
    if (this.beginCallPlayerIndex == this.currentRoundPlayerIndex) {
      this.isEndCall = true

      // 判断是否还需要由第一个【叫地主】的玩家确定地主归属权
      if (this.isAllPlayerNotCallLand()) {
        // 没人叫地主，直接返回null
        return null
      } else if (this.isOnePlayerCallLand()) {
        console.log('this.isOnePlayerCallLand():', true)
        // 只有一人叫地主，并且返回null
        return null
      } else {
        // 2个或以上玩家叫地主，将由第一个执行【叫地主】的玩家决定地主归属权
        this.currentRoundPlayerIndex = this._firstCallLandPlayerIndex
      }
    }

    // 当前回合玩家对象
    const player = this.players[this.currentRoundPlayerIndex]
    if (player === undefined) {
      throw new Error(`[referee] player is undefined`)
    }

    return player
  }

  /**
   * 确定地主
   */
  public determineLandloard() {
    // 没人叫地主
    if (this.isAllPlayerNotCallLand()) {
      this._landlordIndex = -1
      return
    }

    // 地主为最后一个叫地主的玩家
    this._landlordIndex = this._lastCallLandPlayerIndex
    // 回合归属权交给地主
    this.currentRoundPlayerIndex = this._landlordIndex

    // 地主玩家对象
    const player = this.players[this._landlordIndex]
    if (player === undefined) {
      throw new Error(`[referee] player is undefined`)
    }

    // 叫地主流程已经结束，可以通过 landlordPlayer获取地主对应的玩家
    this.stage = GameStage.GAMING

    // 给地主玩家发地主牌
    player.cards.push(...this.landlordCards)

    console.log('_landlordIndex:', this._landlordIndex)
  }

  /**
   * 是否所有玩家都不叫地主
   */
  private isAllPlayerNotCallLand(): boolean {
    return this._firstCallLandPlayerIndex == -1 // 没人叫地主
  }

  /**
   * 是否只有一名玩家叫地主
   */
  private isOnePlayerCallLand(): boolean {
    let callCount = 0
    this.players.forEach(player => {
      if (player && player.callLandloardCount > 0) {
        callCount++
      }
    })

    return callCount === 1 // 只有1人叫地主
  }

  /**
   * 出牌。对牌进行校验，比较大小
   */
  public playCards(uid: string, cards: Array<Poker>) {
    const playerIndex = this.findPlayerIndexById(uid)

    if (playerIndex === -1) {
      throw new Error(`[referee] this user does not exist, user id = ${uid}`)
    }

    // 获得出牌的玩家对象
    const player = this.players[playerIndex]
    if (player === undefined) {
      throw new Error(`[referee] player is undefined`)
    }

    // 每张牌是否唯一，防止客户端发送相同的牌
    if (!PokerUtil.isOnly(cards)) {
      throw new Error('cards are not unique')
    }

    // 校验这个用户是否包含这些牌...
    if (!PokerUtil.contain(player.cards, cards)) {
      throw new Error('abnormal card playing')
    }

    // 若之前没人出牌，就不需要和自己比较
    const currRoundPlayer = this.players[this.currentRoundPlayerIndex]
    if (currRoundPlayer == undefined) {
      throw new Error(`[referee] player is undefined`)
    }
    if (this.prePlayPlayer?.id == uid) {
      this.preCards = []  // 上一个出牌的人是自己，清空上一副牌，使得PokerUtil.legal()可以比较大小
    }

    this.prePlayPlayer = currRoundPlayer

    // 根据牌型，比较是否能压上家的牌
    const legalResult = PokerUtil.legal(this.preCards, cards)
    if (!legalResult) {
      throw new Error('无效牌组，校验失败，或比较大小失败')
    }

    // 校验成功
    // 减少玩家手牌
    cards.forEach(card => {
      let p = 0
      while (p < player.cards.length) {
        if (player.cards[p].isEquals(card)) {
          player.cards.splice(p, 1) // 删除
          continue  // 跳过 p++
        }
        p++
      }
    })

    // 当前玩家出牌完毕，游戏结束
    if (player.cards.length <= 0) {
      this.stage = GameStage.SETTLEMENT
      // 赢家
      // 是否是地主
      this._winPlayer = player                               // 赢家
      if (this.landlordPlayer != null && this.landlordPlayer.id === this._winPlayer.id) {
        this._winPlayerIsLand = true                         // 赢家是否是地主
      }
    }

    // 保存出的牌
    this.preCards = cards
  }

  /**
   * 获取下一个出牌玩家
   */
  public nextPlayCardPlayer(): Player | null {
    // 处于结算中，说明不再存在下一个回合的玩家，游戏已经结束
    if (this.stage == GameStage.SETTLEMENT) {
      return null
    }

    // 往左遍历为逆时针
    this.currentRoundPlayerIndex--
    if (this.currentRoundPlayerIndex < 0) {
      this.currentRoundPlayerIndex = this.players.length - 1
    }

    const player = this.players[this.currentRoundPlayerIndex]
    if (player === undefined) {
      throw new Error(`[referee] player is undefined`)
    }
    return player
  }

  /**
   * 是否是某个玩家的回合
   * @param uid 
   */
  public isRound(uid: string) {
    const player = this.players[this.currentRoundPlayerIndex]
    if (player === undefined) {
      throw new Error(`[referee] player is undefined`)
    }
    if (player.id === uid) {
      return true
    }

    return false
  }

  /**
   * 清空所有准备
   */
  private clearPrepare() {
    this.players.forEach(player => {
      if (player) {
        player.prepare = false
      }
    })
  }

  /**
   * 获取游戏结果，并且将游戏设为准备阶段
   * @returns 结算结果：胜利方、胜利金额...
   */
  public getResult() {
    // 游戏进入准备阶段
    this.stage = GameStage.PREPARATION
    if (this._winPlayer) {
      return {
        // 赢家
        winPlayerId: this._winPlayer.id,
        // 是否是地主
        isLand: this._winPlayerIsLand
      }
    }
    return null
  }

  /**
   * 设置状态修改监听回调函数
   * @param callback 
   */
  public addStageListener(callback: StageCallbackFn | Function) {
    this.stageCallbacks.add(callback)
  }

  /**
   * 通知并调用所有状态修改的回调函数
   * @param stage 
   * @param data 
   */
  private notifyStageCallback(stage: GameStage, data?: any) {
    for (const callback of this.stageCallbacks) {
      callback(stage, data)
    }
  }

}